package socketmvc.core.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import socketmvc.core.context.ConfigContext;
import socketmvc.core.context.IContext;
import socketmvc.core.exception.ProcessorExecException;
import socketmvc.core.exception.UnknownException;
import socketmvc.core.exception.SocketMvcBaseException;
import socketmvc.core.packets.ResponseFuture;
import socketmvc.core.packets.basic.IFilePacket;
import socketmvc.core.packets.basic.IMultiFilePacket;
import socketmvc.core.packets.basic.IPacket;
import socketmvc.core.packets.basic.TextBody;
import socketmvc.core.packets.biz.SockFile;
import socketmvc.core.packets.biz.SockMultiFile;
import socketmvc.core.processor.bean.ProcessorMethod;
import socketmvc.core.processor.bean.ProcessorMethodParam;
import socketmvc.core.processor.bean.ProcessorMethodReturn;
import socketmvc.core.util.ClassUtils;
import socketmvc.core.util.StringUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.List;

/**
 * 命令调度程序过滤器
 * 识别命令码对应的处理器函数并调度执行
 * @author xueyanjun
 */
public class CommandDispatcherFilter extends AbstractFilter {

    private final Logger logger = LoggerFactory.getLogger(CommandDispatcherFilter.class);

    @Override
    public void doFilter(IContext context, Object other, IFilterChain filterChain) {
        IPacket packet = context.getPacket();

        // 同一次请求的响应回复包
        boolean responsePkt = packet.isResponsePkt();
        if (responsePkt){
            ResponseFuture.finish(packet);
            return;
        }

        ProcessorMethod processorMethod = obtainProcessorMethod(context,packet);

        if (processorMethod != null) {
            logger.debug("Command[ {} ], 找到Processor[ {} ]",packet.getCommand(), StringUtils.toStringLazy(processorMethod::getName));
            dispatch(context, packet, processorMethod);
        }else {
            logger.warn("Command[ {} ], 未匹配到任何Processor",packet.getCommand());
        }

        filterChain.doFilter(context, other);
    }

    @Override
    public String path() {
        return "/**";
    }

    @Override
    public short order() {
        return Short.MAX_VALUE;
    }

    /**
     * 获取method
     * @param context 上下文
     * @param packet packet
     * @return method
     */
    protected ProcessorMethod obtainProcessorMethod(IContext context,IPacket packet){
        if (packet == null) {
            throw new IllegalArgumentException("参数异常，未收到客户端报文");
        }
        String command = packet.getCommand();
        if (command == null) {
            throw new IllegalArgumentException("参数异常，报文中必须包含cmd");
        }
        return ConfigContext.getConfig(context.getServerConnInfo()).getProcessorMethodHandler().getProcessorMethod(command);
    }

    /**
     * 获取文本内容
     *
     * @param packet 数据包
     * @return 文本内容json
     */
    protected TextBody obtainTextBodyJSON(IPacket packet) {
        return packet.getTextBodyAsObj();
    }

    /**
     * 获取二进制内容
     *
     * @param packet 数据包
     * @return 二进制内容bytes
     */
    protected byte[] obtainBinBodyBytes(IPacket packet) {
        return packet.getBinBodyAsBytes();
    }


    protected SockFile obtainFile(IPacket packet) {
        if (packet instanceof IFilePacket filePacket){
            return filePacket.toFile();
        }
        return null;
    }

    protected SockMultiFile obtainMultiFile(IPacket packet) {
        if (packet instanceof IMultiFilePacket filePacket){
            return filePacket.toMultiFile();
        }
        return null;
    }

    /**
     * 匹配业务处理器并执行
     *
     * @param context         tcp上下文
     * @param packet          数据包
     * @param processorMethod 业务处理器对象
     */
    protected void dispatch(IContext context, IPacket packet, ProcessorMethod processorMethod) {

        List<ProcessorMethodParam> params = processorMethod.getParams();
        Object[] parameters = new Object[params.size()];

        // 解析packet参数传入到method参数中
        for (int i = 0; i < params.size(); i++) {
            ProcessorMethodParam param = params.get(i);
            if (param.getOptimizer() != null){
                parameters[i] = param.getOptimizer().value(context,packet);
            }else {
                parameters[i] = requestParam(context,packet,param);
            }
        }

        ProcessorMethodReturn methodReturn = processorMethod.getReturn();
        try {
            Object result = processorMethod.invoke(parameters);
            ProcessorMethodReturn.ValOptimizer optimizer = methodReturn.getOptimizer();
            if (optimizer != null){
                IPacket returnPkt = optimizer.value(context, packet, result);
                if (returnPkt != null){
                    context.send(returnPkt);
                }
            }
        }catch (SocketMvcBaseException xe){
            // 响应异常时，直接抛出，避免循环异常
            throw xe;
        }catch (InvocationTargetException ite){
            Throwable ta = ite.getTargetException();
            if (ta instanceof RuntimeException rta){
                throw rta;
            }else {
                throw new ProcessorExecException(ta);
            }
        }catch (Exception e) {
            throw new UnknownException(e);
        }
    }

    @Deprecated
    public Object requestParam(IContext context, IPacket packet,ProcessorMethodParam param){

        TextBody textBodyAsJSON = obtainTextBodyJSON(packet);
        String paramName = param.getName();
        Type paramType = param.getType();
        Class<?> paramClass = param.getClazz();

        if (ClassUtils.classAssignableFrom(paramClass,IPacket.class)) {
            return packet;
        }
        if (ClassUtils.classAssignableFrom(paramClass,IContext.class)) {
            return context;
        }

        // 封装一个ImFile类，用来接受二进制类型
        if (paramClass.equals(SockFile.class)) {
            return obtainFile(packet);
        }

        if (paramClass.equals(SockMultiFile.class)) {
            return obtainMultiFile(packet);
        }

        if (paramName == null || paramName.isEmpty() || paramName.equals("ImParam@ROOT")) {
            return textBodyAsJSON.toJavaObject(paramClass);
        }

        Object parameter = null;
        // 基本数据类型
        if (paramClass.isPrimitive() || paramClass.equals(String.class) || paramClass.isAssignableFrom(Number.class)) {
            String shortName = paramClass.getSimpleName();
            switch (shortName) {
                case "String" -> parameter = textBodyAsJSON.getString(paramName);
                case "int" -> parameter = textBodyAsJSON.getIntValue(paramName);
                case "Integer" -> parameter = textBodyAsJSON.getInteger(paramName);
                case "long" -> parameter = textBodyAsJSON.getLongValue(paramName);
                case "Long" -> parameter = textBodyAsJSON.getLong(paramName);
                case "BigDecimal" -> parameter = textBodyAsJSON.getBigDecimal(paramName);
                case "double" -> parameter = textBodyAsJSON.getDoubleValue(paramName);
                case "Double" -> parameter = textBodyAsJSON.getDouble(paramName);
                case "float" -> parameter = textBodyAsJSON.getFloatValue(paramName);
                case "Float" -> parameter = textBodyAsJSON.getFloat(paramName);
                case "short" -> parameter = textBodyAsJSON.getShortValue(paramName);
                case "Short" -> parameter = textBodyAsJSON.getShort(paramName);
                case "boolean" -> parameter = textBodyAsJSON.getBooleanValue(paramName);
                case "Boolean" -> parameter = textBodyAsJSON.getBoolean(paramName);
                default -> textBodyAsJSON.getObject(paramName, paramClass);
            }
        } else {
            // 非基本数据类型
            parameter = textBodyAsJSON.getObject(paramName, paramType);
        }
        return parameter;
    }


}
